﻿using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using TomanuExtensions;
using RaytracerLib.Layers.Interpolators;
using RaytracerLib.Layers.OverlayCorrectors;

namespace RaytracerLib.Layers
{
    public class ValueArrayFloat : ValueArrayBase
    {
        protected internal float[,] m_values;

        protected ValueArrayFloat(OverlayMethod a_overlayMethod = OverlayMethod.Overlap,
            InterpolationMethod a_interpolationMethod = InterpolationMethod.Bilinear)
            : base(a_overlayMethod, a_interpolationMethod)
        {
        }

        public ValueArrayFloat(Bitmap a_bmp, OverlayMethod a_overlayMethod = OverlayMethod.Overlap,
            InterpolationMethod a_interpolationMethod = InterpolationMethod.Bilinear)
            : base(a_overlayMethod, a_interpolationMethod)
        {
            Create(a_bmp);
        }

        public ValueArrayFloat(int a_width, int a_height,
            OverlayMethod a_overlayMethod = OverlayMethod.Overlap,
            InterpolationMethod a_interpolationMethod = InterpolationMethod.Bilinear)
            : base(a_overlayMethod, a_interpolationMethod)
        {
            Width = a_width;
            Height = a_height;
            m_values = new float[Width, Height];
        }

        protected void Create(Bitmap a_bmp)
        {
            Width = a_bmp.Width;
            Height = a_bmp.Height;
            m_values = new float[Width, Height];

            System.Drawing.Imaging.BitmapData bd = a_bmp.LockBits(
                new Rectangle(0, 0, Width, Height),
                ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

            try
            {
                IntPtr ptr = bd.Scan0;
                int bytes = bd.Stride * a_bmp.Height;
                byte[] values = new byte[bytes];
                System.Runtime.InteropServices.Marshal.Copy(ptr, values, 0, bytes);
                for (int y = 0; y < Height; y++)
                {
                    int src_index = y * bd.Stride;

                    for (int x = 0; x < Width; x++)
                    {
                        m_values[x, y] = (values[src_index++] +
                                          values[src_index++] +
                                          values[src_index++]) / 255.0f / 3.0f;
                    }
                }
            }
            finally
            {
                a_bmp.UnlockBits(bd);
            }
        }

        public override double GetValue(int a_x, int a_y)
        {
            return m_values[a_x, a_y];
        }

        public override void SetValue(int a_x, int a_y, double a_value)
        {
            m_values[a_x, a_y] = (float)a_value;
        }

        public override bool Equals(object a_obj)
        {
            if (Object.ReferenceEquals(a_obj, this))
                return true;

            if (!base.Equals(a_obj))
                return false;

            ValueArrayFloat ar = a_obj as ValueArrayFloat;
            if (ar == null)
                return false;

            return EqualsContent(ar);
        }

        public virtual bool EqualsContent(ValueArrayFloat a_ar)
        {
            if (Width != a_ar.Width)
                return false;
            if (Height != a_ar.Height)
                return false;

            for (int x = 0; x < Width; x++)
            {
                for (int y = 0; y < Height; y++)
                {
                    if (GetValue(x, y) != a_ar.GetValue(x, y))
                        return false;
                }
            }

            return true;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public ValueArrayFloat GetArrayRect(Rectangle a_rect)
        {
            ValueArrayFloat dest = new ValueArrayFloat(a_rect.Width, a_rect.Height);

            for (int y = a_rect.Top; y < a_rect.Bottom; y++)
            {
                for (int x = a_rect.Left; x < a_rect.Right; x++)
                    dest.SetValue(x - a_rect.Left, y - a_rect.Top, GetValue(x, y));
            }

            return dest;
        }

        public override void Fill(double a_value)
        {
            foreach (Point p in Pixels())
                SetValue(p.X, p.Y, a_value);
        }
    }
}